package game.Engine.Entity;

import game.MainController;
import game.Engine.Entity.Magic.MagicType;
import game.display.Animation;
import game.display.Images;
import java.awt.Point;
import java.awt.geom.Point2D;
import java.util.Random;

/**
 * This class represents the aliens within our program
 * Aliens extends Creature. Aliens wake up when they see a player within a certain
 * radius of the alien. They also attack the player by shooting magic.
 * 
 * @author Justin
 *
 */
public class Alien extends Creature {
	private MagicType type;

	private static Random r = new Random();

	private static final double JUMP_SPEED = -8f;
	private static final long SLEEP_TIME = 30000000000L;

	private Animation[] moving, attacking, dying;
	private long count;

	/**
	 * Constructor 
	 * @param p The x,y location of the alien within the game
	 * @param i Random integer to decide what type of alien it is
	 */
	public Alien(Point p, int i) {
		super(0, 0, null);
		points = 100;
		this.x = (double) p.getX();
		this.y = (double) p.getY();
		this.state = State.SLEEPING;
		this.gi = Images.ALIENMOVING.image();
		myLoc = new Point2D.Double();
		MainController.gameEngine.addAlienCount();

		//Animations of the aliens
		moving = new Animation[] { Images.ALIENMOVING.image(),
				Images.ALIENMOVING_RED.image(),
				Images.ALIENMOVING_GREEN.image(),
				Images.ALIENMOVING_BLUE.image() };
		attacking = new Animation[] { Images.ALIENATTACKING.image(),
				Images.ALIENATTACKING_RED.image(),
				Images.ALIENATTACKING_GREEN.image(),
				Images.ALIENATTACKING_BLUE.image() };
		dying = new Animation[] { Images.ALIENDYING.image(),
				Images.ALIENDYING_RED.image(), Images.ALIENDYING_GREEN.image(),
				Images.ALIENDYING_BLUE.image() };

		count = 0;
		setType(i);
	}
	
	/**
	 * Constructor
	 * @param x The x location of the alien
	 * @param y The y location of the alien
	 */
	public Alien(double x, double y) {
		this(new Point((int) x, (int) y), r.nextInt(5));
	}

	/**
	 * Sets the type for the alien by a random integer
	 * @param i The random integer that determines the type of the alien
	 */
	private void setType(int i) {
		if (i == 0)
			type = MagicType.AIR;
		else if (i == 1)
			type = MagicType.FIRE;
		else if (i == 2)
			type = MagicType.EARTH;
		else
			type = MagicType.WATER;
	}

	/**
	 * Gets the max speed at which the aliens can move at
	 */
	public double getMaxSpeed() {
		return 0.05D;
	}

	@Override
	public Animation getDyingImage() {
		return dying[type.getNum()];
	}

	/**
	 * The update method determines when the alien should wake up.
	 * Also, once the alien is awake, it sets the velocity of the alien.
	 * It also determines when the alien should jump and cast at the player
	 */
	@Override
	public void update(long timeElapsed) {
		checkForPlayer();
		if (this.state == Entity.State.SLEEPING)canWakeUpMethod(timeElapsed);
		else if (this.state == Entity.State.NORMAL) {
			// Changes images
			if (MainController.gameEngine.player.getX() > this.x - 3
					&& MainController.gameEngine.player.getX() < this.x + 3)
				gi = attacking[type.getNum()];
			else
				gi = moving[type.getNum()];

			// Checks if its in the air
			if (!isOnGround()) {
				setVelocityY(getVelocityY() + GRAVITY * timeElapsed
						/ 1000000000);
			}

			// Sets the dx for the alien
			if (MainController.gameEngine.player.getX() < this.x)
				setVelocityX(getVelocityX() - 2f * timeElapsed / 1000000000);
			else
				setVelocityX(getVelocityX() + 2f * timeElapsed / 1000000000);

			// Jumps, cast or does nothing
			double i = r.nextDouble();
			if (i < .0001)
				jump();
			if (canCast(timeElapsed))
				cast();

			// If they are trying to move left or right and are stuck, then jump
			if (Math.abs(dx) < .2)
				jump();
		}

		super.update(timeElapsed);
	}

	/**
	 * Determines if the alien can wake up after a certain time period.
	 * The alien wakes up after SLEEP_TIME (30 seconds)
	 * @param timeElapsed The amount of time that elapsed since the last time this
	 * method was called
	 */
	private void canWakeUpMethod(long timeElapsed) {
		if(count > SLEEP_TIME)wakeUp();
		else count += timeElapsed;
	}

	/**
	 * Checks to see if the alien is allowed to cast or not
	 * @param timeElapsed The amount of time that past since this method was last called
	 * @return True if the alien is allowed to cast, false otherwise
	 */
	private boolean canCast(long timeElapsed) {
		lastCast += timeElapsed;
		return lastCast > timeToBeat;
	}

	private long timeToBeat = 1000000;

	private void newTime() {
		lastCast = 0;
		timeToBeat = (long) ((1 + r.nextDouble()) * magicCooldowns[type
				.getNum()]);
	}

	/**
	 * This method checks to see if the alien can see the player.
	 * If the alien can see the player, then it changes its state from SLEEPING to NORMAL
	 */
	public void checkForPlayer() {
		if ((MainController.gameEngine.player.getX() > this.x - 7 && MainController.gameEngine.player
				.getX() < this.x + 7)
				&& (MainController.gameEngine.player.getY() > this.y - 4 && MainController.gameEngine.player
						.getY() < this.y + 4)
				&& (this.state == Entity.State.SLEEPING)) {
			wakeUp();
		}
	}

	/**
	 * The method makes the alien jump. If onGround is true, then the alien
	 * can execute the jump.
	 */
	public void jump() {
		if (isOnGround()) {
			executeJump();
		}
	}

	/**
	 * This method executes the jump for the alien
	 * It sets onGround to false and sets the velocityY to the JUMP_SPEED
	 */
	private void executeJump() {
		setOnGround(false);
		setVelocityY(JUMP_SPEED);
	}

	public long lastCast;
	private Point2D.Double myLoc;

	private int points;

	/**
	 * The method cast the magic for the alien. The location of the cast is determined by 
	 * the player's location. 
	 */
	public void cast() {
		newTime();
		Point2D.Double target = MainController.gameEngine.player.getTarget();
		myLoc.setLocation(x, y);
		MainController.gameEngine.entities.add(new Magic(myLoc, 10, target,
				type, false, getVelocityX(), getVelocityY()));
	}

	/**
	 * Determines if the alien has been hit or not. Depending on the color of the magic,
	 * you will get different points.
	 */
	@Override
	public void hitBy(Magic m) {
		double modifier = getModifier(m.getType());
		hit(modifier * BASE_DAMAGE);
		points = (int)(100 * modifier);
	}

	/**
	 * Gets the modifier for the points the player get for killing an alien with
	 * a certain type of magic
	 * @param type2 MagicType
	 * @return The modifier for the points a player will receive
	 */
	private double getModifier(MagicType type2) {
		if (type2 == MagicType.LIGHTNING) {
			return 4;
		}
		switch (type) {
		case AIR:
			return 1;
		case WATER:
			switch (type2) {
			case FIRE:
				return .5;
			case EARTH:
				return 1.5;
			case WATER:
				return 1;
			case AIR:
				return 1;
			case LIGHTNING:
				return 4;
			}
		case FIRE:
			switch (type2) {
			case FIRE:
				return 1;
			case EARTH:
				return .5;
			case WATER:
				return 1.5;
			case AIR:
				return 1;
			case LIGHTNING:
				return 4;
			}
		case EARTH:
			switch (type2) {
			case FIRE:
				return 1.5;
			case EARTH:
				return 1;
			case WATER:
				return .5;
			case AIR:
				return 1;
			case LIGHTNING:
				return 4;
			}
		}
		return 1;
	}

	/**
	 * Gets the points that the player will receive
	 * @return The number of points
	 */
	public int getPoints() {
		return points;
	}
}
